﻿using LearningWorkbench.Configuration;
using MongoDB.Bson;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LearningWorkbench
{
    public static class InputTargetHandler
    {
        private static bool checkFilter(string type, string value, string onValue)
        {
            try
            {
                switch (type)
                {
                    case "gte":
                        var oval = double.Parse(onValue);
                        var pval = double.Parse(value);

                        return oval >= pval;

                    case "lte":
                        oval = double.Parse(onValue);
                        pval = double.Parse(value);

                        return oval <= pval;

                    case "ew":
                        return onValue.EndsWith(value);
                }
            }
            catch (Exception)
            {
                return false;
            }

            return false;
        }

        private static double applyTransformation(string operation, string value, string onValue)
        {
            switch (operation)
            {
                case "tm":
                    int oval;

                    if (!int.TryParse(onValue, out oval))
                    {
                        throw new InvalidCastException();
                    }

                    var h = oval / 100;
                    var m = oval % 100;
                    return h * 60 + m;
                case "rfe":
                    var val = onValue.Remove(onValue.Length - 1);
                    return double.Parse(val);
            }

            throw new InvalidOperationException();
        }

        private static string getRawValue(string field, LazyBsonDocument document)
        {
            if (field.Contains("."))
            {
                var typeHierarchy = field.Split('.');

                return document[typeHierarchy[0]].
                    AsBsonDocument[typeHierarchy[1]].AsString.Trim();
            }

            return document[field].AsString.Trim();
        }

        public static double[] HandleTargets(LazyBsonDocument document)
        {
            var targets = WorkbenchModel.TargetFieldNames;
            var targetsCount = targets.Count;
            var extendedTargetCount = WorkbenchModel.PrepTargetFieldNames.Count;
            var rawTargets = new double[extendedTargetCount];

            for (int i = 0; i < targetsCount; ++i)
            {
                var target = targets[i];
                var rawField = getRawValue(target, document);

                if (rawField == string.Empty)
                {
                    return null;
                }

                var dquery = from d in WorkbenchModel.ColumnDiscreteFields
                             where d.column == target
                             select d.fields;

                if (dquery.Count() != 0)
                {
                    var fields = dquery.Single();

                    foreach (var col in fields)
                    {
                        var index = WorkbenchModel.PrepTargetFieldNames.IndexOf(col.field);

                        rawTargets[index] = col.values.Contains(rawField) ? 1 : 0;
                    }
                }
            }

            return rawTargets;
        }

        public static double[] HandleInputs(LazyBsonDocument document)
        {
            var inputs = WorkbenchModel.InputFieldNames;
            var inputCount = inputs.Count;
            var extendedInputCount = WorkbenchModel.PrepInputFieldNames.Count;
            var rawInput = new double[extendedInputCount];

            for (int i = 0; i < inputCount; ++i)
            {
                var input = inputs[i];
                var rawField = getRawValue(input, document);

                if (rawField == string.Empty)
                {
                    return null;
                }

                var fquery = from f in WorkbenchModel.ColumnFilters
                             where f.column == input
                             select f.filters;

                if (fquery.Count() != 0)
                {
                    var filters = fquery.Single();

                    foreach (var filter in filters)
                    {
                        if (!checkFilter(filter.type, filter.value, rawField))
                        {
                            return null;
                        }
                    }
                }

                var tquery = from t in WorkbenchModel.ColumnTransformations
                             where t.column == input
                             select t.transformations;

                if (tquery.Count() != 0)
                {
                    var transformations = tquery.Single();
                    var transformed = rawField;

                    foreach (var transformation in transformations)
                    {
                        try
                        {
                            transformed = applyTransformation(transformation.operation, transformation.value,
                                transformed).ToString();
                        }
                        catch (InvalidCastException)
                        {
                            return null;
                        }
                    }

                    rawInput[i] = double.Parse(transformed);
                }
                else
                {
                    rawInput[i] = double.Parse(rawField);
                }

                var nquery = from n in WorkbenchModel.Normalizations
                             where n.field == input
                             select n;

                if (nquery.Count() != 0)
                {
                    var normalization = nquery.Single();

                    rawInput[i] = (rawInput[i] - normalization.origin) / normalization.radius;
                }
            }

            return rawInput;
        }
    }
}
